#include "opm.h"
#include "part.h"

static opm_device opms[OPM_COUNT] = {0};

void opm_sample_timer(void* param);

uint32_t cmd03_01[] = {0x03,0x2C,0x0,0x0,0x01,0x0,0x01,0x0,0xFFFFFFFD,0x0,0xFFFFFBD4};
uint32_t cmd03_08[] = {0x03,0x2C,0x0,0x0,0x08,0x0,0x01,0x0,0xFFFFFFF6,0x0,0xFFFFFBD4};
uint32_t cmd03_09[] = {0x03,0x2C,0x0,0x0,0x09,0x0,0x01,0x0,0xFFFFFFF5,0x0,0xFFFFFBD4};
uint32_t cmd30[]    = {0x30,0x20,0x0,0x0,0x0,0xFFFFFFFF,0x0,0xFFFFFBB3};
uint32_t cmd40[]    = {0x40,0x20,0x0,0x0,0x0,0xFFFFFFFF,0x0,0xFFFFFBA3};

opm_device* init_opm(int no){
    opm_device* p_opm;
    if(no >= OPM_COUNT) return NULL;
    p_opm = &opms[no];
    memset(p_opm,0,sizeof(opm_device));
    if(no == 0){
        p_opm->uart_fd = open_uart("/dev/ttymxc2",115200,8,'N',2);
        p_opm->device_name = "opm_in";
    } 
    else if(no == 1){
        p_opm->uart_fd = open_uart("/dev/ttymxc3",115200,8,'N',2);
        p_opm->device_name = "opm_out";
    } 
    p_opm->timer = timer_start(opm_sample_timer,SAMPLE_TIME,p_opm,1);    
    p_opm->print_flag = print_opm;
    return p_opm;
}

void opm_alarm_register(device_alarm_list* p_list){
    
}

int test_16_to_32(uint16_t* src,size_t size,uint32_t* dst){
    int i=0,j=0;
    for(i=0;i<size;i += 2,j++){
        dst[j] = (src[i]<<16) + src[i+1];
    }
    return j;
}

int get_opm_index(opm_device* p_opm){
    int i = -1;
    for(i=0;i<OPM_COUNT;i++){
        if(p_opm == &opms[i]) break;
    }
    return i;
}

void opm_sample_timer(void* param){
    opm_device* p_opm = (opm_device*)param;
    p_opm->sample_flag = 1;
}

cJSON* opm_packet(int id,int error_code,cJSON* param,int time){
    return response_packet(id,error_code,param,time);
}

cJSON* scan_packet(opm_device* p_opm){
    int i,count;
    cJSON* root = cJSON_CreateObject();
    cJSON* array,*item;
    cJSON_AddItemToObject(root,"object",cJSON_CreateNumber(get_opm_index(p_opm)));
    cJSON_AddItemToObject(root,"temperature",cJSON_CreateNumber(p_opm->temperature));
    cJSON_AddItemToObject(root,"maxPowerOfRawDetectedPowerData",cJSON_CreateNumber(p_opm->max_power_of_raw_detected_power_data));
    cJSON_AddItemToObject(root,"freqWhereMaxPower",cJSON_CreateNumber(p_opm->freq_where_max_power));
    cJSON_AddItemToObject(root,"totalNumberOfChannels",cJSON_CreateNumber(p_opm->total_number_of_channels));
    count = p_opm->total_number_of_channels;
    if(count > 0){
        array = cJSON_CreateArray();
        for(i=0;i<count;i++){
            item = cJSON_CreateObject();
            cJSON_AddItemToObject(item,"peakOpitcalPower1",cJSON_CreateNumber(p_opm->peak_osnr1_optical_power[i]));
            cJSON_AddItemToObject(item,"peakOpitcalFreq",cJSON_CreateNumber(p_opm->peak_freq[i]));
            cJSON_AddItemToObject(item,"peakOsnr",cJSON_CreateNumber(p_opm->peak_osnr[i]));
            cJSON_AddItemToObject(item,"peakOpitcalPower2",cJSON_CreateNumber(p_opm->peak_osnr2_optical_power[i]));
            cJSON_AddItemToArray(array,item);             
        }                   
        cJSON_AddItemToObject(root,"channelsInfo",array);
    }
  
    cJSON_AddItemToObject(root,"totalNumberOfMpoint",cJSON_CreateNumber(p_opm->m_total_number_of_data_spectrumpoints));
    count = p_opm->m_total_number_of_data_spectrumpoints;
    if(count > 0){
        array = cJSON_CreateFloatArray(p_opm->m_points_of_power_of_spectrum,count);
        cJSON_AddItemToObject(root,"mPointsOfPower",array);
        array = cJSON_CreateFloatArray(p_opm->m_points_of_freq_of_the_spectrum,count);
        cJSON_AddItemToObject(root,"mPointsOfFreq",array);
    }
    cJSON_AddItemToObject(root,"totalPowerInTheSpectrum",cJSON_CreateNumber(p_opm->total_power_in_the_spectrum));
    
    return root;
}

cJSON* scanspectrum(opm_device* p_opm, int factor){
    uint32_t p_buf[64];
    uint32_t recv_buff[200*1024];
    int ret,i,count = 0;
    memcpy(p_buf,cmd03_09,sizeof(cmd03_09));
    p_buf[6] = factor;
    p_buf[8] = 0xFFFFFFFF - p_buf[4] - p_buf[6];
    htonls(p_buf,sizeof(cmd03_09)/sizeof(int));
    ret = query_tty((device*)p_opm,(char*)p_buf,sizeof(cmd03_09),(char*)recv_buff,sizeof(recv_buff),5,0);
    if(ret > 0){
        htonls(recv_buff,ret/sizeof(int));
        if(ret != recv_buff[1]){
            return opm_packet(id_opm_scan,OPM_MESSAGE_LENGTH_ERROR,NULL,0);
        }
        
        p_opm->temperature = recv_buff[3];
        p_opm->max_power_of_raw_detected_power_data = recv_buff[5];
        p_opm->freq_where_max_power = recv_buff[6];
        p_opm->total_number_of_channels = recv_buff[7];
        for(i=0;i<p_opm->total_number_of_channels;i++){
            p_opm->peak_osnr1_optical_power[i] = recv_buff[8+i*5];
            p_opm->peak_freq[i] = recv_buff[9+i*5];
            p_opm->peak_osnr[i] = recv_buff[10+i*5];
            p_opm->peak_osnr2_optical_power[i] = recv_buff[11+i*5];
            count += 5;            
        }
     
        p_opm->m_total_number_of_data_spectrumpoints = recv_buff[8+count];
        for(i=0;i<p_opm->m_total_number_of_data_spectrumpoints;i++){
            p_opm->m_points_of_power_of_spectrum[i] = recv_buff[9+count+i];            
        }   
        count += i;
        for(i=0;i<p_opm->m_total_number_of_data_spectrumpoints;i++){
            p_opm->m_points_of_freq_of_the_spectrum[i] = recv_buff[9+count+i];             
        }  
        count += i;
        p_opm->total_power_in_the_spectrum = recv_buff[9+count];
        
        return opm_packet(id_opm_scan,NO_ERROR,scan_packet(p_opm),0);
    }
    return opm_packet(id_opm_scan,COMM_TIMEOUT,NULL,0);
}

cJSON* version_packet(opm_device* p_opm){
    int i,count;
    cJSON* root = cJSON_CreateObject();
    cJSON* array,*item;
    cJSON_AddItemToObject(root,"object",cJSON_CreateNumber(get_opm_index(p_opm)));
    cJSON_AddItemToObject(root,"firmwareVersion",cJSON_CreateString(p_opm->firmware_version));
    cJSON_AddItemToObject(root,"assemblySerialNumber",cJSON_CreateString(p_opm->assembly_serial_number));
    cJSON_AddItemToObject(root,"filterSerialNumber",cJSON_CreateString(p_opm->filter_serial_number));
    return root;
}

cJSON* version_opm(opm_device* p_opm){
    uint32_t p_buf[64];
    uint32_t recv_buff[1024];
    char* p_data;
    int ret,i,count = 0;
    memcpy(p_buf,cmd30,sizeof(cmd30));
    htonls(p_buf,sizeof(cmd30)/sizeof(int));
    ret = query_tty((device*)p_opm,(char*)p_buf,sizeof(cmd30),(char*)recv_buff,sizeof(recv_buff),5,0);
    if(ret > 0){
        htonls(recv_buff,ret/sizeof(int));
        if(ret != recv_buff[1]){
            return opm_packet(id_opm_scan,OPM_MESSAGE_LENGTH_ERROR,NULL,0);
        }
        p_opm->temperature = recv_buff[3];
        p_data = (char*)&recv_buff[13];
        memcpy(p_opm->firmware_version,p_data,37);
        p_opm->firmware_version[37] = 0;
        memcpy(p_opm->assembly_serial_number,p_data+37,20);
        p_opm->firmware_version[20] = 0;
        memcpy(p_opm->filter_serial_number,p_data+57,23);
        p_opm->firmware_version[23] = 0;
        return opm_packet(id_opm_ver,NO_ERROR,scan_packet(p_opm),0);
    }
    return opm_packet(id_opm_scan,COMM_TIMEOUT,NULL,0);
}

cJSON* reset_opm(opm_device* p_opm){
    uint32_t p_buf[64];
    uint32_t recv_buff[128] = {0};
    int ret,i,count = 0;
    memcpy(p_buf,cmd40,sizeof(cmd40));
    htonls(p_buf,sizeof(cmd40)/sizeof(int));
    ret = query_tty((device*)p_opm,(char*)p_buf,sizeof(cmd30),(char*)recv_buff,sizeof(recv_buff),10,0);
    if(ret > 0){
        htonls(recv_buff,ret/sizeof(int));
        if(recv_buff[0] == 0x40)
            return opm_packet(id_opm_reset,NO_ERROR,scan_packet(p_opm),0);
    }
    return opm_packet(id_opm_reset,COMM_TIMEOUT,NULL,0);
}

void sample_opm(void){

}

cJSON* opm_parse(CMD_ID id,cJSON* param){
    cJSON* item;
    int obj,factor = 1;
    int ret = UNKNOWN_ID;
    opm_device* p_opm = NULL;
    cJSON* opm_json;

    if(id >= id_opm_scan && id <= id_opm_reset){
        item = cJSON_GetObjectItem(param,"object");
        if(item){
            obj = item->valueint;
            if(obj < OPM_COUNT) p_opm = &opms[obj];
            else{
                return opm_packet(id,OPM_NO_DEVICE_FOUND,NULL,0);
            }
        }
        else{
            return opm_packet(id,MISSING_PARAM,NULL,0);
        }
        if(p_opm){
            if(p_opm->busy) {
                return opm_packet(id,DEVICE_BUSY,NULL,0);
            }
            p_opm->busy = 1;
            switch(id){
                case id_opm_scan:
                    item = cJSON_GetObjectItem(param,"dataSpectrumDecimationFactor");
                    if(item){
                        factor = item->valueint;
                    }
                    opm_json = scanspectrum(p_opm,factor);      
                break;
                case id_opm_ver:	
                    opm_json = version_opm(p_opm);
                    break;
                case id_opm_reset:	
                    opm_json = reset_opm(p_opm);
                break;
            }
            p_opm->busy = 0;
            return opm_json;
        }       
    }
    return opm_packet(id,UNKNOWN_ID,NULL,0);
}

void* opm_thread(void* param){
    opm_device* p_opm;
    int no = (int)param;
    p_opm = init_opm(no);
    if(p_opm){
        while(!p_opm->exit){
            if(p_opm->sample_flag){
                if(p_opm->busy) continue;
                p_opm->busy = 1;

                p_opm->sample_flag = 0;
                p_opm->busy = 0;
                
            }
            usleep(50*1000);
        }
    }
    return NULL;
}